#pragma once

#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>

template <class T>
class BlockQueue{
    static const int defaultnum = 5;

public:
    BlockQueue(int max_capacity = defaultnum)
    : _max_capacity(max_capacity)
    {
        // 初始化互斥锁和条件变量
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
    }

    T pop(){
        // 加锁
        pthread_mutex_lock(&_mutex);

        // 消费条件：判断队列是否为空
        // if(_q.size() == 0){
        //     pthread_cond_wait(&_c_cond, &_mutex);
        // }
        while(_q.size() == 0){
            pthread_cond_wait(&_c_cond, &_mutex);
        }

        T out = _q.front();         // 消费前需要先满足消费条件 
        _q.pop();         

        // 消费后通知生产者进行生产，唤醒一个等待生产的线程
        pthread_cond_signal(&_p_cond);

        // 释放锁
        pthread_mutex_unlock(&_mutex);
        return out;
    }

    void push(const T& in){
        // 加锁
        pthread_mutex_lock(&_mutex);

        // 生产条件：判断队列是否等于极值---判断也是在访问临界资源
        // if (_q.size() == _max_capacity){
        //     pthread_cond_wait(&_p_cond, &_mutex); // 可能会出现伪唤醒情况
        // }
        // while循环判断，防止出现伪唤醒情况
        while(_q.size() == _max_capacity){
            pthread_cond_wait(&_p_cond, &_mutex);
        }

        // 可以执行生产有两种情况---1.队列没满，条件满足；2.被唤醒
        _q.push(in);               // 生产前需要先满足生产条件

        // 生产后通知消费者进行消费，唤醒一个等待消费的线程
        pthread_cond_signal(&_c_cond);

        // 释放锁
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        // 销毁互斥锁和条件变量
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_c_cond);
        pthread_cond_destroy(&_p_cond);
    }

private:
    std::queue<T> _q;
    // 极值
    int _max_capacity;
    // 互斥锁
    pthread_mutex_t _mutex;
    // 条件变量
    pthread_cond_t _c_cond;
    pthread_cond_t _p_cond;
};